Gomoku  refactoring

현재 서버의 문제점 분석1
- 현재 서버 프로그램의 모든 모듈이 하나의 소스코드(Server.cpp)에 작성
- 확장성 및 재사용성의 증대를 위하여 프로그램의 모듈화 할 필요가 있다
현재 서버의 문제점 분석2
- 현재 접속한 플레이어 (Client)를 저장하는 connection 리스트에서 포인터 원소를 사용하지 않고 있다.
- 한 명의 플레이어 (Client) 정보를 한 번 선언되면 반복적으로 선언하지 않도록 해야 한다.
Client.h
#ifndef GOMOKU_CLIENT_H
#define GOMOKU_CLIENT_H
#include <Winsock.h>
class Client {
private:
int clientID;
int roomID;
SOCKET clientSOCKET;
public:
Client(int clientID, SOCKET clientSocket);
int getClientID();
int getRoomID();
void setRoomID(int roomID);
SOCKET getClientSocket();
};
#endif
Client.cpp
#include "Client.h"
Client::Client(int clientID, SOCKET clientSocket) {
this->clientID = clientID;
this->roomID = -1;
this->clientSOCKET = clientSocket;
}
int Client::getClientID() {
return clientID;
}
int Client::getRoomID() {
return roomID;
}
void Client::setRoomID(int roomID) {
this->roomID = roomID;
}
SOCKET Client::getClientSocket() {
return clientSocket;
}
Util.h
#ifndef GOMOKU_UTIL_H
#define GOMOKU_TUIL_H
using namespace std;
#include <vector>
#include <sstream>
class Util{
public:vector<string> getTokens(string input, char delimiter);
};
#endif
Util.cpp
#include "Util.h"
vector<string> Util::getTokens(string input, char delimiter) {
vector<string> tokens;
istringstream f(input);
string s;
while (getline(f, s, delimiter)) {
tokens.push_back(s);
}
return tokens;
}
Server,h
#ifndef GOMOKU_SERVER_H
#define GOMOKU_SERVER_H
#define _CRT_SECURE_NO_WARNINGS
#pragma comment (lib, "ws2_32.lib")
#include <iostream>
using namespace std;
#include <Winsock.h>
#include <vector>
#include "Util.h"
#include "Client.h"
static class Server {
private:
static SOCKET serverSocket;
static WSAData wsaData;
static SOCKADDR_IN serverAddress;
static int nextID;
static vector<Client*> connections;
static Util util;
public:
static void start();
static int clientCountInRoom(int roomID);
static void playClient(int roomID);
static void exitClient(int roomID);
static void putClient(int roomID, int x, int y);
static void fullClient(Client* client);
static void enterClient(Client* client);
static void enterClient(Client* client);
static void ServerThread(Client* client);
};
#endif
Server.cpp
#include "Server.h"
SOCKET Server::serverSocket;
WSAData Server::wsaData;
SOCKADDR_IN Server::serverAddress;
int Server::nextID;
vector<Client*> Server::connections;
Util Server::util;
void Server::enterClient(Client* client) {
char* sent = new char[25];
ZeroMemory(sent, 256);
sprintf(sent, "%s", "[ENter]");
send(client->getClientSocket(), sent, 256, 0);
}
void Server::fullClient(Client* client) {
char* sent = new char[256];
ZeroMemory(sent, 256);
sprintf(sent, "%s", "[Full]");
send(client->getClientSocket(), sent, 256, 0);
}
void Server::playClient(int roomID){
char* sent = new char[256];
bool black = true;
for (int i = 0; i < connections.size(); i++) {
if (connections[i]->getRoomID() == roomID) {
ZeroMemory(sent, 256);
if (black) {
sprintf(sent, "%s", "[Play]Black");
black = false;
}
else {
sprintf(sent, "%s", "[Play]White");
}
send(connections[i]->getClientSocket(), sent, 256, 0);
}
}
}
void Server::exitClient(int roomID) {
char* sent = new char[256];
for (int i = 0; i < connections.size(); i++) {
if (connections[i]->getRoomID() == roomID) {
ZeroMemory(sent, 256);
sprintf(sent, "%s", "[Exit]");
send(connections[i]->getClientSocket(), sent, 256, 0);
}
}
}
void Server::putClient(int roomID, int x, int y) {
char* sent = new char[256];
for (int i = 0; i < connections.size(); i++) {
if (connections[i]->getRoomID() == roomID) {
ZeroMemory(sent, 256);
string data = "[Put]" + to_string(x) + "," + to_string(y);
sprintf(sent, "%s", data.c_str());
send(connections[i]->getClientSocket(), sent, 256, 0);
}
}
}
int Server::clientCountInRoom(int roomID) {
int count = 0;
for (int i = 0; i < connections.size(); i++) {
if (connections[i]->getRoomID() == roomID) {
count++;
}
}
return count;
}
void Server::start() {
WSAStartup(MAKEWORD(2, 2), &wsaData);
serverSocket = socket(AF_INET, SOCK_STREAM, NULL);
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
serverAddress.sin_port = htons(9876);
serverAddress.sin_family = AF_INET;
cout << "[ C++ ]" << endl;
bind(serverSocket, (SOCKADDR*)&serverAddress, sizeof(serverAddress));
listen(serverSocket, 32);
int addressLength = sizeof(serverAddress);
while (true) {
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, NULL);
if (clientSocket = accept(serverSocket, (SOCKADDR*)&serverAddress, &addressLength)) {
Client* client = new Client(nextID, clientSocket);
cout << "[/ ]" << endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ServerThread, (LPVOID)client, NUL, NULL);
connections.push_back(client);
nextID++;
}
sleep(100);
}
}
void Server::ServerThread(Client* client) {
char* sent = new char[256];
char* received = new char[256];
int size = 0;
while (true) {
ZeroMemory(received, 256);
if ((size = recv(client->getClientSocket(), received, 256, NULL)) > 0) {
string receivedString = string(received);
vector<string> tokens = util.getTokens(receivedString, ']');
if (receivedString.find("[Enter]") != -1) {
string roomID = tokens[1];
int roomInt = atoi(roomID.c_str());
int clientCount = clientCountInRoom(roomInt);
/* 2 */
if (clientCount >= 2) {
fullClient(client);
}
/* */
client->setRoomID(roomInt);
cout << " [" << client->getClientID() << "]: " << client->getRoomID() << " " << endl;
/* */
enterClient(client);
/* */
if (clientCount == 1) {
playClient(roomInt);
}
}
else if (receivedString.find("[Put]") != -1) {
/* */
string data = tokens[1];
vector<string> dataTokens = util.getTokens(data, ',');
int roomID = atoi(dataTokens[0].c_str());
int x = atoi(dataTokens[1].c_str());
int y = atoi(dataTokens[2].c_str());
/* */
putClient(client->getRoomID(), x, y);
}
else if (receivedString.find("[Play]") != -1) {
string roomID = tokens[1];
int roomInt = atoi(roomID.c_str());
/* */
playClient(client->getRoomID());
}
}
else {
cout << "[" << client->getClientID() << "] ." << endl;
/* */
for (int i = 0; i < connections.size(); i++) {
if (connections[i]->getClientID() == client->getClientID()) {
/* */
if (connections[i]->getRoomID() != -1 &&
clientCountInRoom(connections[i]->getRoomID()) == 2) {
/* */
exitClient(connections[i]->getRoomID());
}
connections.erase(connections.begin() + i);
break;
}
}
delete client;
break;
}
}
}
main.cpp
#include "Server.h"
int main(void) {
Server::start();
}
cmd
git status     // 명령어를 통해서 개선되기 이전과 비교해서 변경된 소스 코드 정보를 얻을 수 있다.
git branch development
git checkout development
git branch
git status
git add.        // 모든 내용 git에 반영
git commit -m “Source Code Refactoring”
git status        // 변경 사항이 없다고 떠야한다.
git log

git push —set-upstream origin development
온라인 게임 개발을 위한 기본기
- 온라인 게임 수준의 오목 게임을 만들고 싶으면, 플레이어 오목 경기 관련 DB 구축이 필요하다.
- 경기의 승리 판정 로직을 클라이언트가 아닌 서버에게 위임할 필요가 있다.
성능 최적화를 위하여 Boost.Asio와 같은 네트워크 통신 모듈을 사용할 수 있다.
- 클라이언트 개발을 위해 C#이 아닌 웹을 이용할 수 있다.